/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-05-18     liuchao       the first version
 */
#define LOG_TAG "aliyun"
#include <ulog.h>
#include "aliyun.h"
#include "configuration.h"
#include <mqtt_wrapper.h>
#include <dm_wrapper.h>

#include "rtthread.h"
#include "infra_config.h"

extern void HAL_Printf(const char *fmt, ...);
extern int HAL_Snprintf(char *str, const int len, const char *fmt, ...);

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "infra_types.h"
#include "infra_defs.h"
#include "infra_compat.h"
#include "infra_compat.h"
#ifdef INFRA_MEM_STATS
#include "infra_mem_stats.h"
#endif
#include "dev_model_api.h"
#include "dm_wrapper.h"
#include "cJSON.h"
#include "app_net.h"
#include "rotate.h"

static char s_product_key[64] = { 0 };
static char s_device_name[64] = { 0 };

static iotx_linkkit_dev_meta_info_t s_master_meta_info;

typedef struct
{
    int master_devid;
    int cloud_connected;
    int master_initialized;
} aliyun_user_ctx_t;

static aliyun_user_ctx_t s_aliyun_user_ctx;
#define ALIYUN_MASTER_DEVID            (0)

char* aliyun_product_key()
{
    const char* user_name = configuration_get(CONFIGURATION_MQTT_USER_NAME);
    char* pos = strchr(user_name, '&');
    if (pos)
    {
        memset(s_product_key, 0, sizeof(s_product_key));
        memcpy(s_product_key, pos + 1, strlen(user_name) - (pos - user_name));
        return s_product_key;
    }
    return NULL;
}
char* aliyun_device_name()
{
    const char* user_name = configuration_get(CONFIGURATION_MQTT_USER_NAME);
    char* pos = strchr(user_name, '&');
    if (pos)
    {
        memset(s_device_name, 0, sizeof(s_device_name));
        memcpy(s_device_name, user_name, pos - user_name);
        return s_device_name;
    }
    return NULL;
}

char* aliyun_product_secret()
{
    char* secret = configuration_get(CONFIGURATION_MQTT_PRODUCT_SECRET);
    if (secret[0] == 0xff || secret[1] == 0xff || secret[2] == 0xff)
    {
        return NULL;
    }
    return secret;
}
char* aliyun_device_secret()
{
    char* secret = configuration_get(CONFIGURATION_MQTT_DEVICE_SECRET);
    if (secret[0] == 0xff || secret[1] == 0xff || secret[2] == 0xff)
    {
        return NULL;
    }
    return secret;
}

int aliyun_pub_property_float(const char* name, float v)
{
    if (s_aliyun_user_ctx.cloud_connected == 0)
    {
        LOG_W("aliyun not connected");
        return FAIL_RETURN;
    }
    int ret = 0;

    char property_payload[30] = { 0 };
    HAL_Snprintf(property_payload, sizeof(property_payload), "{\"%s\": %f}", name, v);

    ret = IOT_Linkkit_Report(ALIYUN_MASTER_DEVID, ITM_MSG_POST_PROPERTY, (unsigned char *) property_payload,
            strlen(property_payload));

    LOG_D("Post Float Property %s: %f, Message ID: %d", name, v, ret);

    return ret;
}

int aliyun_pub_property_int32(const char* name, rt_int32_t v)
{
    if (s_aliyun_user_ctx.cloud_connected == 0)
    {
        LOG_W("aliyun not connected");
        return FAIL_RETURN;
    }
    int ret = 0;

    char property_payload[30] = { 0 };
    HAL_Snprintf(property_payload, sizeof(property_payload), "{\"%s\": %ld}", name, v);

    ret = IOT_Linkkit_Report(ALIYUN_MASTER_DEVID, ITM_MSG_POST_PROPERTY, (unsigned char *) property_payload,
            strlen(property_payload));

    LOG_D("Post Int32 Property %s: %d, Message ID: %d", name, v, ret);

    return ret;
}

int aliyun_pub_properties(cJSON* payload_j)
{
    if (s_aliyun_user_ctx.cloud_connected == 0)
    {
        LOG_W("aliyun not connected");
        return FAIL_RETURN;
    }
    int ret = 0;

    char* property_payload = cJSON_PrintUnformatted(payload_j);

    ret = IOT_Linkkit_Report(ALIYUN_MASTER_DEVID, ITM_MSG_POST_PROPERTY, (unsigned char *) property_payload,
            strlen(property_payload));

    LOG_D("Post Properties %s Message ID: %d", property_payload, ret);
    cJSON_free(property_payload);

    return ret;
}

int aliyun_get_property_desired(const char* property, ...)
{
    if (s_aliyun_user_ctx.cloud_connected == 0)
    {
        LOG_W("aliyun not connected while get property desired");
        return FAIL_RETURN;
    }

    int ret = 0;

    cJSON* p_j = cJSON_CreateArray();

    va_list ap;
    va_start(ap, property);
    while (property)
    {
        cJSON* property_j = cJSON_CreateString(property);
        cJSON_AddItemToArray(p_j, property_j);
        property = va_arg(ap, const char*);
    }
    va_end(ap);

    char* payload = cJSON_PrintUnformatted(p_j);
    ret = IOT_Linkkit_Report(ALIYUN_MASTER_DEVID, ITM_MSG_PROPERTY_DESIRED_GET, (unsigned char*) payload,
            strlen(payload));
    cJSON_free(payload);

    cJSON_Delete(p_j);

    return ret;
}

/** cloud connected event callback */
static int user_connected_event_handler(void)
{
    LOG_I("Cloud Connected");
    s_aliyun_user_ctx.cloud_connected = 1;

    return 0;
}

/** cloud disconnected event callback */
static int user_disconnected_event_handler(void)
{
    LOG_I("Cloud Disconnected");
    s_aliyun_user_ctx.cloud_connected = 0;

    return 0;
}

/* device initialized event callback */
static int user_initialized(const int devid)
{
    LOG_I("Device Initialized");
    s_aliyun_user_ctx.master_initialized = 1;

    return 0;
}

/** recv event post response message from cloud **/
static int user_property_set_event_handler(const int devid, const char *request, const int request_len)
{
    int ret = 0;
    // {"pressureGap":10}
    LOG_D("Property Set Received, Request: %s", request);

    cJSON* req_j = cJSON_Parse(request);
    if (req_j)
    {
        cJSON* e = NULL;
        cJSON_ArrayForEach(e, req_j)
        {
            if (strcmp(e->string, "pressureSet") == 0)
            {
                LOG_D("pressureSet: %f", e->valuedouble);
                if (aliyun_pub_property_float("pressureSet", e->valuedouble) > 0)
                {
                    auto_mode_set_baseline(e->valuedouble);
                }
            }
            else if (strcmp(e->string, "pressureGap") == 0)
            {
                if (aliyun_pub_property_float("pressureGap", e->valuedouble) > 0)
                {
                    auto_mode_set_percentage(e->valuedouble);
                }
            }
            else if (strcmp(e->string, "updateInterval") == 0)
            {
                if (aliyun_pub_property_int32("updateInterval", e->valuedouble) > 0)
                {
                    auto_mode_set_update_interval(e->valuedouble);
                }
            }
            else if (strcmp(e->string, "updateDelay") == 0)
            {
                if (aliyun_pub_property_int32("updateDelay", e->valuedouble) > 0)
                {
                    auto_mode_set_update_delay(e->valuedouble);
                }
            }
            else if (strcmp(e->string, "PauseSwitch") == 0)
            {
                if (aliyun_pub_property_int32("PauseSwitch", e->valuedouble) > 0)
                {
                    pause_switch_set(e->valuedouble);
                }
            }
            else if (strcmp(e->string, "GoBack") == 0)
            {
                if (aliyun_pub_property_int32("GoBack", e->valuedouble) > 0)
                {
                    go_back_seconds_set(e->valuedouble);
                }
            }
            else if (strcmp(e->string, "ReleasePressure") == 0)
            {
                if (aliyun_pub_property_int32("ReleasePressure", e->valuedouble) > 0)
                {
                    release_pressure_set(e->valuedouble);
                }
            }
            else if (strcmp(e->string, "ResetCounter") == 0)
            {
                if (e->valuedouble && aliyun_pub_property_int32("ResetCounter", 0) > 0)
                {
                    configuration_reset_counter();
                }
            }
            else if (strcmp(e->string, "LowBAT") == 0)
            {
                if (aliyun_pub_property_float("LowBAT", e->valuedouble) > 0)
                {
                    battery_low_threshold_set(e->valuedouble);
                }
            }
            else
            {
                LOG_W("Unhandled property: %s", e->string);
            }
        }
        cJSON_Delete(req_j);
    }

    return ret;
}

static int user_property_desired_get_reply_event_handler(const char *request, const int request_len)
{
    int ret = 0;
    /*
     * {"pressureGap":{"value":10,"version":1},"pressureSet":{"value":2000,"version":5}}
     */
    LOG_D("Property desired get reply Received, Request: %s", request);

    cJSON* req_j = cJSON_Parse(request);
    if (req_j)
    {
        cJSON* e = NULL;
        cJSON_ArrayForEach(e, req_j)
        {
            cJSON* value_j = cJSON_GetObjectItem(e, "value");
            if (strcmp(e->string, "pressureSet") == 0)
            {
                LOG_D("pressureSet: %f", value_j->valuedouble);
                if (aliyun_pub_property_float("pressureSet", value_j->valuedouble) > 0)
                {
                    auto_mode_set_baseline(value_j->valuedouble);
                }
            }
            else if (strcmp(e->string, "pressureGap") == 0)
            {
                LOG_D("pressureGap: %f", value_j->valuedouble);
                if (aliyun_pub_property_float("pressureGap", value_j->valuedouble) > 0)
                {
                    auto_mode_set_percentage(value_j->valuedouble);
                }
            }
            else if (strcmp(e->string, "updateInterval") == 0)
            {
                LOG_D("updateInterval: %f", value_j->valuedouble);
                if (aliyun_pub_property_int32("updateInterval", value_j->valuedouble) > 0)
                {
                    auto_mode_set_update_interval(value_j->valuedouble);
                }
            }
            else if (strcmp(e->string, "updateDelay") == 0)
            {
                LOG_D("updateDelay: %f", value_j->valuedouble);
                if (aliyun_pub_property_int32("updateDelay", value_j->valuedouble) > 0)
                {
                    auto_mode_set_update_delay(value_j->valuedouble);
                }
            }
            else if (strcmp(e->string, "PauseSwitch") == 0)
            {
                if (aliyun_pub_property_int32("PauseSwitch", value_j->valuedouble) > 0)
                {
                    pause_switch_set(value_j->valuedouble);
                }
            }
            else if (strcmp(e->string, "LowBAT") == 0)
            {
                if (aliyun_pub_property_float("LowBAT", value_j->valuedouble) > 0)
                {
                    battery_low_threshold_set(value_j->valuedouble);
                }
            }
            else
            {
                LOG_W("Unhandled property: %s", e->string);
            }
        }
        cJSON_Delete(req_j);
    }

    return ret;
}

static void aliyun_entry()
{
    int res = 0;
    int domain_type = 0, dynamic_register = 0, post_reply_need = 0;

    is_ethernet_up(5000);

    IOT_SetLogLevel(IOT_LOG_WARNING);

    /* Register Callback */
    IOT_RegisterCallback(ITE_CONNECT_SUCC, user_connected_event_handler);
    IOT_RegisterCallback(ITE_DISCONNECTED, user_disconnected_event_handler);
    // IOT_RegisterCallback(ITE_SERVICE_REQUEST, user_service_request_event_handler);
    IOT_RegisterCallback(ITE_PROPERTY_SET, user_property_set_event_handler);
    // IOT_RegisterCallback(ITE_REPORT_REPLY, user_report_reply_event_handler);
    // IOT_RegisterCallback(ITE_TRIGGER_EVENT_REPLY, user_trigger_event_reply_event_handler);
    // IOT_RegisterCallback(ITE_TIMESTAMP_REPLY, user_timestamp_reply_event_handler);
    IOT_RegisterCallback(ITE_INITIALIZE_COMPLETED, user_initialized);
    // IOT_RegisterCallback(ITE_FOTA, user_fota_event_handler);
    // IOT_RegisterCallback(ITE_COTA, user_cota_event_handler);
    IOT_RegisterCallback(ITE_PROPERTY_DESIRED_GET_REPLY, user_property_desired_get_reply_event_handler);

    domain_type = IOTX_CLOUD_REGION_SHANGHAI;
    IOT_Ioctl(IOTX_IOCTL_SET_DOMAIN, (void *) &domain_type);

    /* Choose Login Method */
    dynamic_register = 0;
    IOT_Ioctl(IOTX_IOCTL_SET_DYNAMIC_REGISTER, (void *) &dynamic_register);

    /* post reply doesn't need */
    post_reply_need = 1;
    IOT_Ioctl(IOTX_IOCTL_RECV_EVENT_REPLY, (void *) &post_reply_need);

    /* Create Master Device Resources */
    s_aliyun_user_ctx.master_devid = IOT_Linkkit_Open(IOTX_LINKKIT_DEV_TYPE_MASTER, &s_master_meta_info);
    if (s_aliyun_user_ctx.master_devid < 0)
    {
        LOG_E("IOT_Linkkit_Open Failed");
        return;
    }

    /* Start Connect Aliyun Server */
    res = IOT_Linkkit_Connect(s_aliyun_user_ctx.master_devid);
    if (res < 0)
    {
        LOG_E("IOT_Linkkit_Connect Failed");
        return;
    }

    while (1)
    {
        IOT_Linkkit_Yield(200);
        rt_thread_mdelay(1000);
    }

#if 0
    IOT_Linkkit_Close(s_aliyun_user_ctx.master_devid);

    IOT_DumpMemoryStats(IOT_LOG_DEBUG);
    IOT_SetLogLevel(IOT_LOG_NONE);
#endif
}

static int aliyun_init(void)
{
    ulog_tag_lvl_filter_set(LOG_TAG, LOG_LVL_DBG);
    char* product_key = aliyun_product_key();
    char* device_name = aliyun_device_name();
    char* product_secret = aliyun_product_secret();
    char* device_secret = aliyun_device_secret();
    if (product_key == NULL || device_name == NULL || product_secret == NULL || device_secret == NULL)
    {
        LOG_E("Invalid aliyun parameter");
        return RT_EOK;
    }

    HAL_SetProductKey(product_key);
    HAL_SetDeviceName(device_name);
    HAL_SetProductSecret(product_secret);
    HAL_SetDeviceSecret(device_secret);

    memset(&s_aliyun_user_ctx, 0, sizeof(s_aliyun_user_ctx));

    memset(&s_master_meta_info, 0, sizeof(iotx_linkkit_dev_meta_info_t));
    memcpy(s_master_meta_info.product_key, product_key, strlen(product_key));
    memcpy(s_master_meta_info.product_secret, product_secret, strlen(product_secret));
    memcpy(s_master_meta_info.device_name, device_name, strlen(device_name));
    memcpy(s_master_meta_info.device_secret, device_secret, strlen(device_secret));

    rt_thread_t t = rt_thread_create("ali", aliyun_entry, NULL, 4096, 100, 10000);
    rt_thread_startup(t);

    return 0;
}
INIT_APP_EXPORT(aliyun_init);
